package server

import (
	"fmt"
	"log"

	"gitee.com/yanwc/ebike-xiaoan-device/device"
	"gitee.com/yanwc/ebike-xiaoan-device/device/config"
	uppacket "gitee.com/yanwc/ebike-xiaoan-device/device/packet/up"
	"gitee.com/yanwc/ebike-xiaoan-device/internal/codec"
	"gitee.com/yanwc/ebike-xiaoan-device/internal/field"
	"github.com/panjf2000/gnet"
)

type ServerHandler struct {
	gnet.EventServer
	config.ServerConfig
}

func (es *ServerHandler) OnClosed(c gnet.Conn, err error) (action gnet.Action) {
	if d, ok := device.SessionDevices.GetWithConn(c); ok {
		if es.OnLogoutNotify != nil {
			go es.OnLogoutNotify(config.Context{
				Imei:            d.Imei,
				Imsi:            d.Imsi,
				FirmwareVersion: d.Version,
			})
		}

		device.SessionDevices.Remove(d.Imei)
		log.Printf("%s@%s关闭连接", d.Imei, c.RemoteAddr().String())
		return gnet.Close
	}

	log.Printf("%s关闭连接", c.RemoteAddr().String())
	return gnet.Close
}

// 响应接收的数据帧
func (es *ServerHandler) React(packet []byte, c gnet.Conn) (out []byte, action gnet.Action) {
	log.Printf("%#v\n", packet)
	fh := codec.NewEmptyFrame()
	err := fh.UnWrapPacket(packet)
	if !fh.CRCCheck() {
		return nil, gnet.None
	}

	if err != nil {
		log.Fatal(err)
	}

	if fh.Header.CmdWord == field.LoginCommandWord {
		lp := uppacket.LoginPacket{}
		out, action, err := lp.Login(fh, c, es.ServerConfig)
		if err != nil {
			return out, gnet.Close
		} else {
			return out, action
		}

	} else {
		if v, ok := uppacket.Packets[fh.Header.CmdWord]; ok {
			// 此处需要使用异步执行 启动goroutine 避免主循环等待
			if r, action, err := v.Do(fh, c); err != nil {
				return r, gnet.Close
			} else {
				return r, action
			}
		} else {
			log.Fatalf("命令字%d不支持,数据帧:%#v", fh.Header.CmdWord, fh.RawData())
		}
	}

	return fh.WrapPacket(nil), gnet.Shutdown
}

type Server struct {
	config.ServerConfig
}

func (s *Server) Start() {
	handler := &ServerHandler{
		ServerConfig: config.ServerConfig{
			Host:              s.Host,
			Port:              s.Port,
			OnEventNotify:     s.OnEventNotify,
			OnStatusNotify:    s.OnStatusNotify,
			OnHeartBeatNotify: s.OnHeartBeatNotify,
			OnLoginNotify:     s.OnLoginNotify,
			OnLogoutNotify:    s.OnLogoutNotify,
		},
	}
	endPoint := fmt.Sprintf("tcp://%s:%d", s.Host, s.Port)
	log.Printf("Started Server At:%s", endPoint)

	gnet.Serve(handler,
		endPoint,
		gnet.WithMulticore(true),
		gnet.WithCodec(&codec.BikeFrameCodec{}))
}
